#ifndef JMETA_H
#define JMETA_H
///////////////
/// 元系统
///////////////
#include <JXQCore/JMetaType>
#include <JXQCore/JMetaFunction>
#include <JXQCore/JString>

namespace JXQ
{

class JXQLIB_EXPORT JMeta
{
	J_PERMANENT_SINGLETON(JMeta)

public:
	// 注册类型（带参数的构造函数）
	template <typename Type, typename ...Args>
	static size_t registerType(std::function<void*(Args...)> &&constructMethod,
							   const std::string &typeName = "");
	// 注册类型
	template <typename Type>
	static size_t registerType(const std::string &typeName = "");

	// 注册类非静态方法
	template <typename Class, typename Return, typename...Args>
    static size_t registerFunc(const std::string &name, J_INVOKE Return(Class::*method)(Args...));

	// 注册全局函数
	template <typename Return, typename...Args>
    static size_t registerFunc(const std::string &name, J_INVOKE Return(*func)(Args...));

public:
	// 获取类型信息
	static const JTypeInfo &info(const std::string &typeName);

	// 获取函数类型（字符串）
	inline static std::string funcTypeName(size_t classTypeId, const std::string &name);
	inline static std::string funcTypeName(const std::string &name);

public:
	// 反射 构造对象
	template <typename ...Args>
	static void *newObject(const std::string &typeName, Args&&...args);

	template <typename ...Args>
	static void *newObject(size_t typeID, Args&&...args);

	// 反射 拷贝构造对象
	inline static void *copyObject(const std::string &typeName, const void *beCopied);
	static void *copyObject(size_t typeID, const void *beCopied);

	// 反射 赋值拷贝对象
	inline static bool assignment(const std::string &typeName, void *src, const void *beCopied);
	static bool assignment(size_t typeID, void *src, const void *beCopied);

	// 反射 析构对象
	inline static bool deleteObject(const std::string &typeName, const void *obj);
	static bool deleteObject(size_t typeID, const void *obj);

public: // 调用函数
	template <typename Return, typename...Args>
	static Return invoke(const void *obj, const std::string &className,
                         const std::string &name, Args...args);

	template <typename Return, typename...Args>
    static Return invoke(const std::string &name, Args...args);

public: // 非阻塞调用函数（线程池）
	template <typename Return, typename...Args>
	static std::future<Return> invokeNoBlock
		(const void *obj, const std::string &className,
         const std::string &name, Args...args);

	template <typename CallBack, typename Return, typename...Args>
	static std::future<Return> invokeNoBlock
	(CallBack &&callBack, const void *obj, const std::string &className,
         const std::string &name, Args...args);

	template <typename Return, typename...Args>
	static std::future<Return> invokeNoBlock
    (const std::string &name, Args...args);

	template <typename CallBack, typename Return, typename...Args>
	static std::future<Return> invokeNoBlock
    (CallBack &&callBack, const std::string &name, Args...args);

public:
	// 获取类型ID（哈希值）
	static size_t getTypeId(const std::string &typeName);

	// 是否注册
	inline static bool isTypeRegister(const std::string &typeName);
	static bool isTypeRegister(size_t id);

	inline static bool isFuncRegister(size_t classTypeId, const std::string &name);
	inline static bool isFuncRegister(const std::string &name);

private:
	typedef JOtherMethodHash::DestructMethod  DestructMethod;
	typedef JOtherMethodHash::CopyConstructMethod  CopyConstructMethod;

	template <typename Type>
	static size_t __registerType(const std::string &typeName);

	static size_t verify(const std::string &typeName);

	template <typename Func, typename ...Args>
	static void *callConstructMethod(Func &func, Args&&...args);
};

// 注册类型
#define J_DECLARE_TYPE_ARGS(Type, declName, ...)                               \
	static const J_DECL_USED size_t __global_jxq_Type_Id_1_##declName =         \
	JXQ::JMeta::registerType<Type,__VA_ARGS__>(std::function<void*(__VA_ARGS__)> \
	(JXQ::jTypeConstructHelper<Type,__VA_ARGS__>));

#define J_DECLARE_TYPE(Type, declName)                                 \
	static const J_DECL_USED size_t __global_jxq_Type_Id_1_##declName = \
	JXQ::JMeta::registerType<Type>();

#define J_NAME_DECLARE_TYPE_ARGS(Type, customName, declName, ...)              \
	static const J_DECL_USED size_t __global_jxq_Type_Id_1_##declName =         \
	JXQ::JMeta::registerType<Type,__VA_ARGS__>(std::function<void*(__VA_ARGS__)> \
	(JXQ::jTypeConstructHelper<Type,__VA_ARGS__>), customName);

#define J_NAME_DECLARE_TYPE(Type, customName, declName)                \
	static const J_DECL_USED size_t __global_jxq_Type_Id_1_##declName = \
	JXQ::JMeta::registerType<Type>(customName);

// 注册函数
#define J_DECLARE_INVOKE_NAME(name, declName, func) \
	static const J_DECL_USED                        \
	size_t __global_jxq_Method_##declName =         \
	JXQ::JMeta::registerFunc(name, func);

#define J_DECLARE_INVOKE(declName, func)  \
	J_DECLARE_INVOKE_NAME(#func, declName, func)

template <typename Type, typename ...Args>
inline size_t JMeta::registerType(std::function<void*(Args...)> &&constructMethod,
									  const std::string &typeName)
{
	size_t id = __registerType<Type>(typeName);
	typedef typename JConstructionMethodHash<Args...>::ConstructMethod ConstructMethod;
	JConstructionMethodHash<Args...>::instance().
			registerMethod(id, std::forward<ConstructMethod>(constructMethod));
	return id;
}

template <typename Type>
inline size_t JMeta::registerType(const std::string &typeName)
{
	size_t id = __registerType<Type>(typeName);
	JConstructionMethodHash<>::instance().
			registerMethod(id, JXQ::jTypeConstructHelper<Type>);
	return id;
}

template <typename Class, typename Return, typename...Args>
inline size_t JMeta::
registerFunc(const std::string &name, J_INVOKE Return(Class::*method)(Args...))
{
	size_t id = JFuncInfo::registerFunc(name, method);
	JFunctionHash<Args...>::instance().registerFunc(id, method);
	return id;
}

template <typename Return, typename...Args>
inline size_t JMeta::
registerFunc(const std::string &name, J_INVOKE Return(*func)(Args...))
{
	size_t id = JFuncInfo::registerFunc(name, func);
	JFunctionHash<Args...>::instance().registerFunc(id, func);
	return id;
}

inline std::string JMeta::
	funcTypeName(size_t classTypeId, const std::string &name)
{
	return JFuncInfo::getInfo(classTypeId, name);
}

inline std::string JMeta::
	funcTypeName(const std::string &name)
{
	return JFuncInfo::getInfo(name);
}

template <typename ...Args>
inline void *JMeta::newObject(const std::string &typeName, Args&&...args)
{
	return newObject(verify(typeName), std::forward<Args>(args)...);
}

template <typename ...Args>
inline void *JMeta::newObject(size_t typeID, Args&&...args)
{
	if( typeID == 0 )
		return nullptr;

	// 构造方法
	return callConstructMethod(JConstructionMethodHash<Args...>::instance().
							   findConstructMethod(typeID),
							   std::forward<Args>(args)...);
}

inline void *JMeta::copyObject(const std::string &typeName, const void *beCopied)
{
	return copyObject(verify(typeName), beCopied);
}

inline bool JMeta::
assignment(const std::string &typeName, void *src, const void *beCopied)
{
	// 赋值拷贝对象
	return assignment(verify(typeName), src, beCopied);
}

inline bool JMeta::deleteObject(const std::string &typeName, const void *obj)
{
	return deleteObject(verify(typeName), obj);
}

template <typename Return, typename...Args>
Return JMeta::invoke(const void *obj, const std::string &className,
                           const std::string &name, Args...args)
{
	const size_t classTypeId = getTypeId(className);
	if( classTypeId == 0 )
		return Return();

	if( obj == nullptr )
	{
		jError() << "JMeta::invoke: object is NULL";
		return Return();
	}

	size_t id = JFuncInfo::find(classTypeId, name);
	if( id == 0 )
	{
		jError() << "JMeta::invoke: class method unregistered: '" + name + "'";
		return Return();
	}

    auto method = JFunctionHash<Args...>::instance().template findClassMethod<Return>
                  (id, obj, std::forward<Args>(args)...);

	if( method == nullptr )
	{
		jError() << "JMeta::invoke: class method parameter mismatch: '" + name + ": " + JFuncInfo::getInfo(id) + "'";
		return Return();
    }
    return method();
}

template <typename Return, typename...Args>
Return JMeta::invoke(const std::string &name, Args...args)
{
	size_t id = JFuncInfo::find(name);
	if( id == 0 )
	{
		jError() << "JMeta::invoke: func unregistered: '" + name + "'";
		return Return();
	}

    auto func = JFunctionHash<Args...>::instance().template findGlobalFunc<Return>
                (id, std::forward<Args>(args)...);

	if( func == nullptr )
	{
		jError() << "JMeta::invoke: func parameter mismatch: '" + name + ": " + JFuncInfo::getInfo(id) + "'";
		return Return();
	}
    return func();
}

template <typename Return, typename...Args>
inline std::future<Return> JMeta::
invokeNoBlock(const void *obj, const std::string &className,
                  const std::string &name, Args...args)
{
	return JConcurrent::run<Return(const void*, const std::string&, const std::string&, Args...),
			const void*, const std::string&, const std::string&, Args...>
		(invoke<Return, Args...>, obj, className, name, std::forward<Args>(args)...);
}

template <typename CallBack, typename Return, typename...Args>
std::future<Return> JMeta::
	invokeNoBlock(CallBack &&callBack, const void *obj, const std::string &className,
                  const std::string &name, Args...args)
{
	return JConcurrent::run<CallBack, Return(const void*, const std::string&, const std::string&, Args...),
			const void*, const std::string&, const std::string&, Args...>
		(std::forward<CallBack>(callBack), invoke<Return, Args...>,
		 obj, className, name, std::forward<Args>(args)...);
}

template <typename Return, typename...Args>
std::future<Return> JMeta::
invokeNoBlock(const std::string &name, Args...args)
{
	return JConcurrent::run<Return(const std::string&, Args...), const std::string&, Args...>
			(invoke<Return, Args...>, name, std::forward<Args>(args)...);
}

template <typename CallBack, typename Return, typename...Args>
std::future<Return> JMeta::
invokeNoBlock(CallBack &&callBack, const std::string &name, Args...args)
{
	return JConcurrent::run<CallBack, Return(const std::string&, Args...), const std::string&, Args...>
			(std::forward<CallBack>(callBack), invoke<Return, Args...>,
			 name, std::forward<Args>(args)...);
}

inline bool JMeta::isTypeRegister(const std::string &typeName)
{
	return isTypeRegister(getTypeId(typeName));
}

inline bool JMeta::
	isFuncRegister(size_t classTypeId, const std::string &name)
{
	return JFuncInfo::find(classTypeId, name) != 0;
}

inline bool JMeta::
	isFuncRegister(const std::string &name)
{
	return JFuncInfo::find(name) != 0;
}

template <typename Type>
inline size_t JMeta::__registerType(const std::string &typeName)
{
	const auto realName = typeName.empty()? JTypeInfo::getName<Type>() : typeName;
	const size_t id = typeid(Type).hash_code();
	JTypeInfo::typeIdHash.emplace(realName, id);

	if( JTypeInfo::typeInfoHash.emplace(id, &jtypeid<Type>()).second == false )
		return id;

	JOtherMethodHash::
			registerCopyMethod(id, JXQ::jTypeCopyHelper<Type>);
	JOtherMethodHash::
			registerCopyConstructMethod(id, JXQ::jTypeCopyConstructHelper<Type>);
	JOtherMethodHash::
			registerDestructMethod(id, JXQ::jTypeDestructHelper<Type>);
	return id;
}

template <typename Func, typename ...Args>
inline void *JMeta::callConstructMethod(Func &func, Args&&...args)
{
	if( func != nullptr )
		return func(std::forward<Args>(args)...);
	return nullptr;
}

} //namespace JXQ


#endif // JMETA_H
